package com.example.project.service.impl;

import com.example.framework.baseService.TokenService;
import com.example.project.constant.Constant;
import com.example.project.entity.UserInfoBean;
import com.example.project.mapper.UserMapper;
import com.example.project.service.LoginService;
import com.example.utils.md5.MD5Util;
import com.example.utils.redis.RedisUtils;
import com.example.utils.resultFormat.Result;
import com.example.utils.resultFormat.ResultUtil;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.dao.DataAccessException;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.stereotype.Service;

import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.Map;

/**
 * @author admin
 * @Cacheable 在方法执行前Spring先查看缓存中是否有数据，如果有数据，则直接返回缓存数据；没有则调用方法并将方法返回值放进缓存。
 * @CachePut 将方法的返回值放到缓存中。
 * @CacheEvict 删除缓存中的数据。
 */
@Service
@CacheConfig(cacheNames = "UserServiceImpl")
public class LoginServiceImpl implements LoginService {

    @Autowired
    UserMapper userMapper;

    @Autowired
    TokenService tokenService;

    @Autowired
    public RedisUtils redisCache;

    /**
     *  用户注册
     * * @param user 用户注册信息
     * * @return 用户注册结果
     */
    public Result addUser(UserInfoBean user) throws DataAccessException {
        try {
            UserInfoBean userForBase = userMapper.getInfoByUsername(user);
            if (userForBase == null) {//验证用户名是否存在
                // 密码加密
                Map<String, String> generate = MD5Util.generate(user.getPassword());
                user.setSalt(generate.get("salt"));
                user.setPassword(generate.get("newPassword"));
                userMapper.insert(user);
            } else {
                return ResultUtil.error("用户名已经存在");
            }
        } catch (DataAccessException e) {
            return ResultUtil.error("已经存在该用户名或者用户昵称，或者用户权限出错");
        }
        return ResultUtil.success("注册成功");
    }

    /**
     * 登录
     *
     * @param requestUser
     * @param request
     * @param response
     * @return
     */
    @Override
    public Result login(UserInfoBean requestUser, HttpServletRequest request, ServletResponse response) {
        //根据用户名获取用户信息
        UserInfoBean userForBase = userMapper.getInfoByUsername(requestUser);

        //获取当前登录用户
        Subject subject = SecurityUtils.getSubject();
        //将接收密码与该用户在数据库中的密码校验
        if (!MD5Util.verify(requestUser.getPassword(), userForBase.getPassword())) {
            return ResultUtil.error("登录失败,密码错误");
        } else {
            //封装用户登录的数据，以便校验
            //MD5Util.verify(password, userForBase.getPassword())?userForBase.getPassword():password
            //将加密的赋值给接收，解决UsernamePasswordToken 无法将密码与加密的密码校验的问题
            UsernamePasswordToken subjectInfo = new UsernamePasswordToken(requestUser.getUserName(), MD5Util.verify(requestUser.getPassword(), userForBase.getPassword()) ? userForBase.getPassword() : requestUser.getPassword());
            try {//成功
                subject.login(subjectInfo);//subject对象匹配登录密码
                // 登录成功 将 Token 写入到 Response 的 Header，方便前端刷新 Token 从 Header 取值
                String token = tokenService.getToken(userForBase);
                HttpServletResponse httpServletResponse = (HttpServletResponse) response;
                httpServletResponse.setStatus(HttpServletResponse.SC_OK);
                httpServletResponse.addHeader(Constant.AUTHORIZATION_HEADER, token);

                userForBase.setToken(token);
                userForBase.setLoginTime(new Date());

                redisCache.set("Bearer " + token, userForBase);
                redisCache.expire(token, 60 * 10);//秒为单位

                return ResultUtil.success(userForBase);//不分离就是页面跳转 return "index"，分离是跳转状态码
            } catch (UnknownAccountException e) {//用户名不存在
                return ResultUtil.error("登录失败,用户不存在");//返回登陆界面login
            } catch (IncorrectCredentialsException e) {//密码错误
                return ResultUtil.error("登录失败,密码错误");//返回登陆界面login
            }
        }
    }

    @Override
    public void exitLogin(UserInfoBean user) {
        String token = tokenService.getToken(user);
        redisCache.deleteObject(token);
    }
}
